package com.zhouzhou.fastjava.vfs;

import com.zhouzhou.fastjava.vfs.exception.VFSException;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

/**
 * @author Mike Liu Thread Safe
 */
public class FtpVFS extends AbstractVFS {

    private final String host;

    private final String user;

    private final String password;

    private final int port;

    private final ThreadLocal<FTPClient> TL_Client = new ThreadLocal<>();

    public FtpVFS(String host, int port, String user, String password) {
        super();
        this.host = host;
        this.user = user;
        this.password = password;
        this.port = port;
    }

    @Override
    protected void storeInternal(InputStream is, String dir, String filename) {
        this.assumeClient();
        try {
            FTPClient client = TL_Client.get();
            client.changeWorkingDirectory(dir);
            client.enterLocalActiveMode();
            client.storeFile(filename, is);
        } catch (IOException e) {
            throw new VFSException("Can't store " + dir + filename, e);
        }
    }

    @Override
    protected void mkdirs(String dir) {
        this.assumeClient();
        try {
            if (isDirectoryExists(dir)) {
                return;
            }
            String parentDir = getParentDir(dir);
            mkdirs(parentDir);
            TL_Client.get().makeDirectory(dir);
        } catch (IOException e) {
            throw new VFSException("Can't mkdirs " + dir, e);
        }
    }

    @Override
    protected void clean(String dir, String filename) {
        super.clean(dir, filename);
        FTPClient client = TL_Client.get();
        if (client != null) {
            if (client.isConnected()) {
                try {
                    client.disconnect();
                } catch (IOException e) {
                    // ignore
                }
            }
            TL_Client.remove();
        }
    }

    private void assumeClient() {
        if (TL_Client.get() == null) {
            TL_Client.set(this.newFtpClient());
        }
    }

    private boolean isDirectoryExists(String dirPath) throws IOException {
        FTPClient client = TL_Client.get();
        client.changeWorkingDirectory(dirPath);
        int returnCode = client.getReplyCode();
        return !(returnCode == 550);
    }

    private String getParentDir(String dir) {
        String parentDir = dir.substring(0, dir.lastIndexOf("/"));
        if (StringUtils.isNotBlank(parentDir)) {
            return parentDir;
        }
        return "/";
    }

    private FTPClient newFtpClient() {
        FTPClient client;
        try {
            client = new FTPClient();
            client.connect(host, port);
            client.login(user, password);
            client.enterLocalPassiveMode();
            client.setBufferSize(1024);
            client.setControlEncoding("UTF-8");
            client.setFileType(FTP.BINARY_FILE_TYPE);
            return client;
        } catch (Exception e) {
            throw new VFSException("Can't init ftp client", e);
        }
    }

    public void close(FTPClient client) {
        if (client != null) {
            if (client.isConnected()) {
                try {
                    client.disconnect();
                } catch (IOException e) {
                    // skip
                }
            }
        }
    }

    @Override
    public InputStream retrieve(String uri) throws VFSException, FileNotFoundException {

        FTPClient client = newFtpClient();
        try {
            InputStream is = client.retrieveFileStream(uri);
            return is;
        } catch (IOException e) {
            throw new VFSException("retrieve file error", e);
        } finally {
            close(client);
        }
    }

}